;;(pushnew #P"/root/slime/" asdf:*central-registry*)
;;(pushnew #P"/root/lisp/ucw/ucw_0.3.7/systems/" asdf:*central-registry*)
;(load "/root/lisp/ucw/ucw_0.3.7/init")

(in-package :it.bese.ucw)

;; Application
;;;;;;;;;;;;;;

(defapplication numbers-application 
  (:url-prefix "/ucw/numbers/")
  (:tal-roots *ucw-tal-root*)
  (:www-roots
   (make-pathname :name nil :type nil
		  :directory (append (pathname-directory *ucw-tal-root*)
				     (list "ucw" "numbers"))
		  :defaults *ucw-tal-root*)))

;; Top level, which handles a window
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defcomponent toplevel (simple-window-component)
  ((body :component simple-container :accessor body))
  (:default-initargs
   :title "Numbers Program (Odd? Even? Factorial?)")
  (:entry-point "index.ucw"))

(defmethod render-on ((res response) (window toplevel))
  (<:h1 (<:as-html "Numbers Program (Odd? Even? Factorial?)"))
  (render-on res (body window)))

(defmethod shared-initialize :after ((app toplevel) slot-names &rest initargs)
  "Initialize the components contained in (body APP)."
  (declare (ignore slot-names initargs))
  (let ((container (body app)))
    (setf (find-component container 'do-number-repl)
	  (make-instance 'do-number-repl))
    (setf (container.current-component-name container)
	  'do-number-repl)))

;; Reading a number
;;;;;;;;;;;;;;;;;;;

(defcomponent read-a-number (widget-component)
  ((current-number :accessor current-number
		   :component (integer-field))))

(defmethod render-on ((res response) (reader read-a-number))
  (let ((number-string ""))
    (<ucw:form :action (ok reader number-string)
      (<:table
       (<:tr (<:td "Number: ")
	     (<:td (<ucw:input :type "text" :accessor number-string))))
      (<:p (<:input :type "submit" :value "Okay")))))

(defaction ok ((reader read-a-number) &optional number-string)
  (let ((num (parse-integer number-string :junk-allowed t)))
    (when num (answer num))))

;; Displaying number properties
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defcomponent display-number-properties ()
  ((number :initarg :number :accessor number)))

(defun factorial (x)
  (let ((answer 1))
    (loop for n from x downto 1
	  do (setf answer (* answer n)))
    answer))

(defun factors (number)
  "Return a list of the factors of a number"
  (when (integerp number)
    (loop for x from 1 to (floor (sqrt number))
	  if (integerp (/ number x))
	  collect (list x (/ number x)))))

(defmethod render-on ((res response) (display display-number-properties))
  (with-slots (number) display
    (<:h2 (<:as-html (format nil "Properties of ~D" number)))
    (<:table :border "1"
     (<:tr (<:td "Parity")
	   (<:td (<:as-html (cond ((evenp number) "Even")
				  ((oddp number) "Odd")
				  (t "Apparently not even or odd")))))
     (<:tr (<:td "Factorial")
	   (<:td (<:as-html (factorial number))))
     (<:tr (<:td "Factors")
	   (<:td (dolist (factor (factors number))
		   (<:as-html (format nil "~D * ~D"
				      (first factor)
				      (second factor)))
		   (<:br)))))))

;; Top level control flow
;;;;;;;;;;;;;;;;;;;;;;;;;

(defcomponent do-number-repl (task-component)
  ())

(defaction start ((repl do-number-repl))
  (call 'display-number-properties
	:number (call 'read-a-number)))

;(defentry-point "index.ucw" (:application numbers-application) ()
;		(call 'toplevel))